package com.toy.core.security.access;

import java.util.Iterator;
import java.util.List;

import com.toy.core.security.Authentication;
import com.toy.core.security.access.voter.AccessDecisionVoter;
import com.toy.core.security.chain.FilterInvocation;
import com.toy.core.security.exception.AuthenticationException;

public class AccessDecisionManagerImpl implements AccessDecisionManager {
	  // ~ Instance fields
    // ================================================================================================
	private List<AccessDecisionVoter> decisionVoters;
	
	/**
	 * 是否允许投票全部弃权
	 */
	private boolean allowIfAllAbstainDecisions = false;
	
	 // ~ Methods
    // ========================================================================================================

	public void decide(Authentication authentication,
			FilterInvocation filterInvocation) throws AccessDeniedException,AuthenticationException {
		Iterator<AccessDecisionVoter> iter = decisionVoters.iterator();
        int deny = 0;

        while (iter.hasNext()) {
            AccessDecisionVoter voter = iter.next();
            int result = voter.vote(authentication, filterInvocation);

            switch (result) {
            case AccessDecisionVoter.ACCESS_GRANTED:
                return;

            case AccessDecisionVoter.ACCESS_DENIED:
                deny++;

                break;

            default:
                break;
            }
        }

        if (deny > 0) {
            throw new AccessDeniedException("Access is denied");
        }

        // To get this far, every AccessDecisionVoter abstained
        checkAllowIfAllAbstainDecisions();

	}

	public void setDecisionVoters(List<AccessDecisionVoter> decisionVoters) {
		this.decisionVoters = decisionVoters;
	}
	
	protected final void checkAllowIfAllAbstainDecisions() {
        if (!this.isAllowIfAllAbstainDecisions()) {
            throw new AccessDeniedException("Access is denied");
        }
    }
	
	public boolean isAllowIfAllAbstainDecisions() {
        return allowIfAllAbstainDecisions;
    }

    public void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) {
        this.allowIfAllAbstainDecisions = allowIfAllAbstainDecisions;
    }
}
